using System;
using System.Collections;
using System.Globalization;
using System.Xml;
using com.paypal.sdk.exceptions;
using com.paypal.sdk.util;

namespace com.paypal.sdk.core
{
	/// <summary>
	/// Parse settings defined in the paypal XML config section.  The results are in the public
	/// readonly fields.
	/// </summary>
	internal class ConfigSectionParser
	{
		private readonly XmlNode configSection;
		
		/// <summary>
		/// True if the trustAll tag was defined.
		/// </summary>
		public readonly bool IsTrustAllSpecified = false;

		/// <summary>
		/// The boolean value parsed from the TrustAll tag.  Only read this tag if IsTrustAllSpecified is true
		/// </summary>
		public readonly bool TrustAll = false;
		
		/// <summary>
		/// True if the maximumRetries tag was defined.
		/// </summary>
		public readonly bool IsMaximumRetriesSpecified = false;

		/// <summary>
		/// The boolean value parsed from the TrustAll tag.  Only read this tag if IsTrustAllSpecified is true
		/// </summary>
		public readonly int MaximumRetries = 0;
		public readonly bool IsEndpointsSpecified = false;
		public readonly Hashtable Endpoints = new Hashtable();

		public ConfigSectionParser(XmlNode configSection)
		{
			if (configSection == null)
			{
				throw new ArgumentNullException("configSection");
			}
			this.configSection = configSection;
			string trustAllSettingValue = GetSettingValue("trustAll", configSection);
			if (trustAllSettingValue != null)
			{
				try
				{
					this.TrustAll = bool.Parse(trustAllSettingValue);
					this.IsTrustAllSpecified = true;
				}
				catch
				{
				}
			}
			string maximumRetriesSettingValue = GetSettingValue("maximumRetries", configSection);
			if (maximumRetriesSettingValue != null)
			{
				try
				{
					this.MaximumRetries = int.Parse(maximumRetriesSettingValue);
					this.IsMaximumRetriesSpecified = true;
				}
				catch
				{
				}
			}
			XmlNode endpointsXmlNode = GetEndpointsNodeFromConfig();
			if (endpointsXmlNode != null)
			{
				try
				{
					this.Endpoints = ReadEndpoints(endpointsXmlNode);
					this.IsEndpointsSpecified = true;
				}
				catch
				{
				}
			}
		}

		private static string GetSettingValue(string tagName, XmlNode configSection)
		{
			string value = null;
			if (configSection != null)
			{
				XmlNode node = configSection.SelectSingleNode(tagName + "/@value");
				if (node != null)
					value = node.Value;
			}
			return value;
		}

		private XmlNode GetEndpointsNodeFromConfig()
		{
			XmlNode endpointsNodeFromConfig = null;
			if (configSection != null)
			{
				endpointsNodeFromConfig = configSection.SelectSingleNode("endpoints");
			}

			return endpointsNodeFromConfig;
		}

		/// <summary>
		/// Read the API endpoints
		/// </summary>
		public static Hashtable ReadEndpoints(XmlNode root)
		{
			if (root == null)
			{
				throw new ArgumentNullException("node");
			}
			Hashtable endpoints = new Hashtable();

			XmlNodeList environmentNodes = root.SelectNodes("wsdl/environment");
				foreach (XmlNode environmentNode in environmentNodes)
				{
					string environment = environmentNode.SelectSingleNode("@name").Value;
					foreach (XmlNode portNode in environmentNode.SelectNodes("port"))
					{
						bool isThreeToken = IsThreeToken(portNode);
						XmlNode portNameAttributeNode = portNode.SelectSingleNode("@name");
						
						if (NonNull(portNameAttributeNode))
						{
							string port = portNameAttributeNode.Value;
							string url = portNode.InnerText;
							string endpointKey = GetEndpointKey(environment, port, isThreeToken);
							Endpoint endpoint = new Endpoint(environment, port, isThreeToken, url);
							endpoints.Add(endpointKey, endpoint);
						}
					}
				}

			return endpoints;
		}

		private static bool IsThreeToken(XmlNode environmentNode)
		{
			bool isThreeToken = false;
			try
			{
				isThreeToken = bool.Parse(environmentNode.SelectSingleNode("@threetoken").Value);
			}
			catch
			{
			}
			return isThreeToken;
		}

		public static string GetEndpointKey(string environment, string port, bool isThreeToken)
		{
			return environment.ToLower() + "-" + port.ToLower() + ( isThreeToken ? "-threeToken" : string.Empty );
		}


		private static double ParseDouble(string minVersionStr)
		{
			return Double.Parse(minVersionStr, NumberFormatInfo.InvariantInfo);
		}

		/// <summary>
		/// Check a set of objects are non-null
		/// </summary>
		/// <param name="args">list of objects to check</param>
		/// <returns>true if all objects in args are non-null </returns>
		private static bool NonNull(params object[] args)
		{
			foreach (object arg in args)
			{
				if (arg == null)
				{
					return false;
				}
			}
			return true;
		}
	}
}
